package com.weave.model;

import java.util.*;
import java.io.*;
import java.util.Observable;

public final class GameModel extends Observable {

    public static final int WORD_LENGTH = 4;
    private static final String DEFAULT_START = "AAHS";
    private static final String DEFAULT_TARGET = "ABLS";


    private String startWord;
    private String targetWord;
    private final Set<String> dictionary = new HashSet<>();
    private final List<String> steps = new ArrayList<>();
    private boolean gameWon = false;




    private boolean validateInput = true;
    private boolean showSolutionPath = false;
    private boolean randomWords = false;

    public String getStartWord() {
        return startWord;
    }

    public void setStartWord(String startWord) {
        this.startWord = startWord;
    }

    public String getTargetWord() {
        return targetWord;
    }

    public void setTargetWord(String targetWord) {
        this.targetWord = targetWord;
    }

    public Set<String> getDictionary() {
        return dictionary;
    }

    public boolean isGameWon() {
        return gameWon;
    }

    public void setGameWon(boolean gameWon) {
        this.gameWon = gameWon;
    }

    public boolean isValidateInput() {
        return validateInput;
    }

    public void setValidateInput(boolean validateInput) {
        this.validateInput = validateInput;
    }

    public boolean isShowSolutionPath() {
        return showSolutionPath;
    }

    public void setShowSolutionPath(boolean showSolutionPath) {
        this.showSolutionPath = showSolutionPath;
    }

    public boolean isRandomWords() {
        return randomWords;
    }

    public void setRandomWords(boolean randomWords) {
        this.randomWords = randomWords;
    }

    public GameModel() {
        loadDictionary();
        initializeGame(false);
    }

    private void loadDictionary() {
        try (Scanner sc = new Scanner(new File("dictionary.txt"))) {
            while (sc.hasNext()) {
                String word = sc.next().trim().toUpperCase();
                if (word.length() == WORD_LENGTH) {
                    dictionary.add(word);
                }
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Dictionary file not found", e);
        }
    }

    public void initializeGame(boolean forceReset) {
        if (randomWords || forceReset) {
            selectRandomWords();
        } else {
            startWord = DEFAULT_START;
            targetWord = DEFAULT_TARGET;
        }

        steps.clear();
        steps.add(startWord);
        gameWon = false;
        setChanged();
        notifyObservers();
    }

    private void selectRandomWords() {
        List<String> words = new ArrayList<>(dictionary);
        Collections.shuffle(words);

        startWord = words.get(0);
        targetWord = words.get(1);
        while (startWord.equals(targetWord)) {
            targetWord = words.get(2);
        }
    }

    public void processInput(String input) {
        Objects.requireNonNull(input, "Input cannot be null");

        String normalized = input.trim().toUpperCase();
        if (isValidStep(normalized)) {
            steps.add(normalized);
            checkWinCondition(normalized);
            setChanged();
            notifyObservers();
        } else if (validateInput) {
            throw new IllegalArgumentException("It must be four letters, Invalid word: " + input);
        }
    }

    private boolean isValidStep(String word) {
        if (word.length() != WORD_LENGTH) return false;
        if (!dictionary.contains(word)) return false;

        String lastWord = getCurrentWord();
        return countLetterDifferences(lastWord, word) == 1;
    }

    private int countLetterDifferences(String a, String b) {
        assert a.length() == WORD_LENGTH && b.length() == WORD_LENGTH;

        int differences = 0;
        for (int i = 0; i < WORD_LENGTH; i++) {
            if (a.charAt(i) != b.charAt(i)) differences++;
        }
        return differences;
    }

    private void checkWinCondition(String word) {
        gameWon = word.equals(targetWord);
        if (gameWon && showSolutionPath) {
            System.out.println("Solution Path: " + steps);
        }
    }
    public List<String> getSteps() {
        return new ArrayList<>(steps);
    }

    public String getCurrentWord() {
        return steps.get(steps.size() - 1);
    }


    public void setValidationFlag(boolean validate) {
        this.validateInput = validate;
    }

    public void setShowPathFlag(boolean showPath) {
        this.showSolutionPath = showPath;
    }

    public void setRandomModeFlag(boolean randomMode) {
        this.randomWords = randomMode;
    }
}